package com.jh.fcsm.util.word;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFonts;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTMarkupRange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;

import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Stream;

/***
 * 操作word段落
 */
public class WordParagraphsUtils {
    public static final Logger logger = LoggerFactory.getLogger(WordParagraphsUtils.class);
    /**
     * 替换所有段落中的标记
     *
     * @param xwpfParagraphList
     * @param params
     */
    public static void replaceInAllParagraphs(List<XWPFParagraph> xwpfParagraphList, Map<String, Object> params) throws Exception {
        for (XWPFParagraph paragraph : xwpfParagraphList) {
            if (paragraph.getText() == null || paragraph.getText().equals("")){
                continue;
            }
            for (Map.Entry<String,Object> entry : params.entrySet()) {
                String placeHolder = "${" + entry.getKey() + "}";
                if (paragraph.getText().contains(placeHolder)) {
                    replaceInParagraph(paragraph, placeHolder, params.get(entry.getKey()));
                }
            }
        }
    }

    /**
     * 替换所有段落中的标记
     *
     * @param xwpfParagraphList
     * @param params
     */
    public static void replaceInAllParagraphs2(List<XWPFParagraph> xwpfParagraphList, Map<String, String> params) throws Exception {
        for (XWPFParagraph paragraph : xwpfParagraphList) {
            if (paragraph.getText() == null || paragraph.getText().equals("")){
                continue;
            }
            for (Map.Entry<String, String> entry : params.entrySet()) {
                String placeHolder = "${" + entry.getKey() + "}";
                if (paragraph.getText().contains(placeHolder)) {
                    replaceInParagraph(paragraph, placeHolder, params.get(entry.getKey()));
                }
            }
        }
    }

    /**
     * 替换段落中的字符串
     *
     * @param xwpfParagraph
     * @param oldString
     * @param newObj
     */
    public static void replaceInParagraph(XWPFParagraph xwpfParagraph, String oldString, Object newObj) throws Exception {
        Map<String, Integer> pos_map = findSubRunPosInParagraph(xwpfParagraph, oldString);
        if (pos_map != null) {
            List<XWPFRun> runs = xwpfParagraph.getRuns();
            XWPFRun modelRun = runs.get(pos_map.get("end_pos"));
            XWPFRun xwpfRun = xwpfParagraph.insertNewRun(pos_map.get("end_pos") + 1);

            if (newObj instanceof Map) {
                CustomXWPFDocument doc = (CustomXWPFDocument) xwpfRun.getDocument();
                Map pic = (Map) newObj;
                int width = Integer.parseInt(pic.get("width").toString());
                int height = Integer.parseInt(pic.get("height").toString());
                int picType = getPictureType(pic.get("type").toString());
                int isfloat = 0;
                int leftOffset = 0;
                int topOffset = 0;
                boolean behind = false;
                if (pic.get("isfloat") != null) {
                    isfloat = Integer.parseInt(pic.get("isfloat").toString());
                    leftOffset = Integer.parseInt(pic.get("leftOffset").toString());
                    topOffset = Integer.parseInt(pic.get("topOffset").toString());
                    behind = Integer.parseInt(pic.get("behind").toString()) != 0;
                }
                //获取图片流，因本人项目中适用流
                String byteArray = (String) pic.get("content");
                if (isfloat == 1) {
                    insertPictureFloat(xwpfRun, byteArray, width, height, picType, leftOffset, topOffset, behind);
                } else {
                    CTInline inline = xwpfRun.getCTR().addNewDrawing().addNewInline();
                    insertPicture(doc, byteArray, inline, width, height, picType);
                    xwpfRun.setText("");
                }
            } else {
                // System.out.println("字体大小：" + modelRun.getFontSize());
                if (modelRun.getFontSize() != -1) {
                    xwpfRun.setFontSize(modelRun.getFontSize());//默认值是五号字体，但五号字体getFontSize()时，返回-1
                }
                // System.out.println("字体：" + modelRun.getFontFamily());
                String fontFamily = modelRun.getFontFamily();

                if (isChinise(fontFamily)) {
                    // 新建 字体配置对象
                    CTFonts font = xwpfRun.getCTR().addNewRPr().addNewRFonts();
                    //中文
                    font.setEastAsia(fontFamily);
                } else {
                    xwpfRun.setFontFamily(modelRun.getFontFamily());
                }
                xwpfRun.setBold(modelRun.isBold());
                xwpfRun.setCapitalized(modelRun.isCapitalized());
                xwpfRun.setCharacterSpacing(modelRun.getCharacterSpacing());
                xwpfRun.setDoubleStrikethrough(modelRun.isDoubleStrikeThrough());
                xwpfRun.setEmbossed(modelRun.isEmbossed());
                Object ob = modelRun.getEmphasisMark();
                if (ob != null) {
                    String v = String.valueOf(ob);
                    xwpfRun.setEmphasisMark(v);
                }
                xwpfRun.setImprinted(modelRun.isImprinted());
                xwpfRun.setItalic(modelRun.isItalic());
                xwpfRun.setKerning(modelRun.getKerning());
                xwpfRun.setLang(modelRun.getLang());
                xwpfRun.setShadow(modelRun.isShadowed());
                xwpfRun.setSmallCaps(modelRun.isSmallCaps());
                xwpfRun.setStrikeThrough(modelRun.isStrikeThrough());
                xwpfRun.setStyle(modelRun.getStyle());
                xwpfRun.setSubscript(modelRun.getSubscript());
                xwpfRun.setTextHighlightColor(modelRun.getTextHightlightColor() == null ? null : modelRun.getTextHightlightColor().toString());
                xwpfRun.setTextPosition(modelRun.getTextPosition());
                xwpfRun.setTextScale(modelRun.getTextScale());
                xwpfRun.setUnderlineColor(modelRun.getUnderlineColor());
                xwpfRun.setUnderlineThemeColor(modelRun.getUnderlineThemeColor() == null ? null : modelRun.getUnderlineThemeColor().toString());
                xwpfRun.setVanish(modelRun.isVanish());
                xwpfRun.setVerticalAlignment(modelRun.getVerticalAlignment() == null ? null : modelRun.getVerticalAlignment().toString());
                xwpfRun.setColor(modelRun.getColor());
                xwpfRun.setUnderline(modelRun.getUnderline());

                // xwpfRun.setText(newObj == null ? "" : newObj.toString());
                // 处理textarea换行
                String value = null == newObj ? "" : String.valueOf(newObj);
                if (null != value && (value.contains("\r\n") || value.contains("\n"))) {
                    String[] arr = value.split("\\n|\\r\\n");
                    Stream.of(arr).forEach(str -> {
                        xwpfRun.setText(str);
                        xwpfRun.addBreak();
                    });
                } else {
                    xwpfRun.setText(value, 0);
                }
            }
            for (int i = pos_map.get("end_pos"); i >= pos_map.get("start_pos"); i--) {
                xwpfParagraph.removeRun(i);
            }
        }
    }

    private static void insertPictureFloat(XWPFRun xwpfRun, String fileUrl, int width, int height, int picType
            , int leftOffset, int topOffset, boolean behind) throws Exception {

        byte[] bytes = IOUtils.getByteGET(fileUrl);

        InputStream in = new ByteArrayInputStream(bytes);//设置图片路径
        if (width <= 0) {
            width = 100;
        }
        if (height <= 0) {
            height = 100;
        }
        //创建Random类对象
        Random random = new Random();
        //产生随机数
        int number = random.nextInt(999) + 1;

        xwpfRun.addPicture(in, picType, "Seal" + number, Units.toEMU(width), Units.toEMU(height));
        in.close();
        // 2. 获取到图片数据
        CTDrawing drawing = xwpfRun.getCTR().getDrawingArray(0);
        CTGraphicalObject graphicalobject = drawing.getInlineArray(0).getGraphic();

        //拿到新插入的图片替换添加CTAnchor 设置浮动属性 删除inline属性
        CTAnchor anchor = getAnchorWithGraphic(graphicalobject, "Seal" + number,
                Units.toEMU(width), Units.toEMU(height),//图片大小
                Units.toEMU(leftOffset), Units.toEMU(topOffset), behind);//相对当前段落位置 需要计算段落已有内容的左偏移
        drawing.setAnchorArray(new CTAnchor[]{anchor});//添加浮动属性
        drawing.removeInline(0);//删除行内属性
    }

    /**
     * @param ctGraphicalObject 图片数据
     * @param deskFileName      图片描述
     * @param width             宽
     * @param height            高
     * @param leftOffset        水平偏移 left
     * @param topOffset         垂直偏移 top
     * @param behind            文字上方，文字下方
     * @return
     * @throws Exception
     */
    public static CTAnchor getAnchorWithGraphic(CTGraphicalObject ctGraphicalObject,
                                                String deskFileName, int width, int height,
                                                int leftOffset, int topOffset, boolean behind) {
        System.out.println(">>width>>" + width + "; >>height>>>>" + height);
        String anchorXML =
                "<wp:anchor xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" "
                        + "simplePos=\"0\" relativeHeight=\"0\" behindDoc=\"" + ((behind) ? 1 : 0) + "\" locked=\"0\" layoutInCell=\"1\" allowOverlap=\"1\">"
                        + "<wp:simplePos x=\"0\" y=\"0\"/>"
                        + "<wp:positionH relativeFrom=\"column\">"
                        + "<wp:posOffset>" + leftOffset + "</wp:posOffset>"
                        + "</wp:positionH>"
                        + "<wp:positionV relativeFrom=\"paragraph\">"
                        + "<wp:posOffset>" + topOffset + "</wp:posOffset>" +
                        "</wp:positionV>"
                        + "<wp:extent cx=\"" + width + "\" cy=\"" + height + "\"/>"
                        + "<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>"
                        + "<wp:wrapNone/>"
                        + "<wp:docPr id=\"1\" name=\"Drawing 0\" descr=\"" + deskFileName + "\"/><wp:cNvGraphicFramePr/>"
                        + "</wp:anchor>";

        CTDrawing drawing = null;
        CTAnchor anchor=null;
        try {
            drawing = CTDrawing.Factory.parse(anchorXML);
            anchor = drawing.getAnchorArray(0);
            anchor.setGraphic(ctGraphicalObject);
        } catch (XmlException e) {
            logger.error("写入图片失败");
        }
        return anchor;
    }

    /**
     * 找到段落中子串的起始XWPFRun下标和终止XWPFRun的下标
     *
     * @param xwpfParagraph
     * @param substring
     * @return
     */
    public static Map<String, Integer> findSubRunPosInParagraph(XWPFParagraph xwpfParagraph, String substring) {

        List<XWPFRun> runs = xwpfParagraph.getRuns();
        int start_pos = 0;
        int end_pos = 0;
        StringBuilder subtemp=new StringBuilder();
        for (int i = 0; i < runs.size(); i++) {
            subtemp.setLength(0);
            start_pos = i;
            for (int j = i; j < runs.size(); j++) {
                if (runs.get(j).getText(runs.get(j).getTextPosition()) == null){
                    continue;
                }
                subtemp.append(runs.get(j).getText(runs.get(j).getTextPosition()));
                if (subtemp.toString().equals(substring)) {
                    end_pos = j;
                    Map<String, Integer> map = new HashMap<String, Integer>();
                    map.put("start_pos", start_pos);
                    map.put("end_pos", end_pos);
                    return map;
                }
            }
        }
        return null;
    }

    /***
     * 判断字符串是否包含中文
     * @param fontFamily
     * @return
     */
    private static boolean isChinise(String fontFamily) {
        if (StringUtils.isBlank(fontFamily)) {
            return false;
        } else {
            char[] chars = fontFamily.toCharArray();
            for (char c : chars
            ) {
                boolean chinese = isChinese(c);
                if (chinese) {
                    return chinese;
                }
            }
            return false;
        }
    }

    /**
     * 判断是否为中文字符
     *
     * @param c
     * @return
     */
    public static boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS;
    }

    /**
     * 获取图片对应类型代码
     *
     * @param picType
     * @return int
     */
    private static int getPictureType(String picType) {
//        logger.info("WordUtils.getPictureType");
        int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
        if (picType != null) {
            if (picType.equalsIgnoreCase("png")) {
                res = CustomXWPFDocument.PICTURE_TYPE_PNG;
            } else if (picType.equalsIgnoreCase("dib")) {
                res = CustomXWPFDocument.PICTURE_TYPE_DIB;
            } else if (picType.equalsIgnoreCase("emf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_EMF;
            } else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) {
                res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
            } else if (picType.equalsIgnoreCase("wmf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_WMF;
            }
        }
        return res;
    }

    /**
     * insert Picture
     *
     * @param document
     * @param filePath
     * @param inline
     * @param width
     * @param height
     * @throws FileNotFoundException
     */
    private static void insertPicture(/*XWPFDocument*/CustomXWPFDocument document, String filePath,
                                                      CTInline inline, int width,
                                                      int height, int imgType) throws Exception {
//        logger.info("WordUtils.insertPicture");
        //通过流获取图片，因本人项目中，是通过流获取
        //document.addPictureData(imgFile,imgType);
//        HttpURLConnection conn = null;

//            logger.info("WordUtils.insertPicture,filePath="+filePath);
        byte[] bytes = IOUtils.getByteGET(filePath);

        document.addPictureData(bytes, imgType);
        int id = document.getAllPictures().size() - 1;
        final int EMU = 9525;
        width *= EMU;
        height *= EMU;

        String blipId = document.getRelationId(document.getAllPictures().get(id));

        String picXml = getPicXml(blipId, width, height);
        XmlToken xmlToken = null;
        xmlToken = XmlToken.Factory.parse(picXml);
        inline.set(xmlToken);
        inline.setDistT(0);
        inline.setDistB(0);
        inline.setDistL(0);
        inline.setDistR(0);
        CTPositiveSize2D extent = inline.addNewExtent();
        extent.setCx(width);
        extent.setCy(height);
        CTNonVisualDrawingProps docPr = inline.addNewDocPr();
        docPr.setId(id);
        docPr.setName("IMG_" + id);
        docPr.setDescr("IMG_" + id);
    }

    /**
     * get the xml of the picture
     *
     * @param blipId
     * @param width
     * @param height
     * @return
     */
    private static String getPicXml(String blipId, int width, int height) {
        String picXml =
                "" + "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" +
                        "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
                        "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
                        "         <pic:nvPicPr>" + "            <pic:cNvPr id=\"" + 0 +
                        "\" name=\"Generated\"/>" + "            <pic:cNvPicPr/>" +
                        "         </pic:nvPicPr>" + "         <pic:blipFill>" +
                        "            <a:blip r:embed=\"" + blipId +
                        "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>" +
                        "            <a:stretch>" + "               <a:fillRect/>" +
                        "            </a:stretch>" + "         </pic:blipFill>" +
                        "         <pic:spPr>" + "            <a:xfrm>" +
                        "               <a:off x=\"0\" y=\"0\"/>" +
                        "               <a:ext cx=\"" + width + "\" cy=\"" + height +
                        "\"/>" + "            </a:xfrm>" +
                        "            <a:prstGeom prst=\"rect\">" +
                        "               <a:avLst/>" + "            </a:prstGeom>" +
                        "         </pic:spPr>" + "      </pic:pic>" +
                        "   </a:graphicData>" + "</a:graphic>";
        return picXml;
    }

    public static void removeParaListBookMark(List<XWPFParagraph> paraList) {
        if (paraList != null) {
            for (XWPFParagraph para : paraList) {
                removeParagraphBookMark(para);
            }
        }
    }

    ////删除段落内所有的书签 去掉w:bookmarkStart/w:bookmarkEnd
    public static void removeParagraphBookMark(XWPFParagraph para) {
        List<CTBookmark> bookmarkStartList = para.getCTP()
                .getBookmarkStartList();
        if (bookmarkStartList == null) {
            return;
        }
        for (int i = bookmarkStartList.size() - 1; i >= 0; i--) {
            Node bookStartNode = bookmarkStartList.get(i).getDomNode();
            bookStartNode.getParentNode().removeChild(bookStartNode);
        }
        List<CTMarkupRange> bookmarkEndList = para.getCTP()
                .getBookmarkEndList();
        if (bookmarkEndList == null) {
            return;
        }
        for (int i = bookmarkEndList.size() - 1; i >= 0; i--) {
            Node bookEndNode = bookmarkEndList.get(i).getDomNode();
            bookEndNode.getParentNode().removeChild(bookEndNode);
        }

    }
}